import ModuleCollection from "./module";
import { forEachValue } from "./utils";
/**
 * 1. 实现 vuex 插件，将 main.js 注入的 store 变为每个组件的 $store
 * 实现 state getters  actions  mutations
 */
let Vue;

/**
 * 注册所有的 actions mutations getters
 * @param {*} store store 属性   总的， 最外层的
 * @param {*} path 构造递归的栈结构
 * @param {*} module 安装的模块
 */
const installModule = (store, path, module, rootState) => {

  // 计算当前的一个命名空间， 在订阅的时候 每个 key 前面都增加一个。 有的 合并 有的不合并
  // root -> 从根开始查找 path 路径 [a, c]  =>  a/c
  let namespaced = store._module.getNamespace(path) // 从根开始查找 路径拼接
  console.log(namespaced, 88)

  // 循环子模块 [a, b, c]
  if (path.length > 0) {
    // 模块    给rootState 添加模块的属性
    let parent = path
      .slice(0, -1)
      .reduce((memo, current) => memo[current], rootState);
    // 后续数据可能动态注册的模块，如果原来没有，新增了，需要试图更新    parent a  a.b  a.b.c
    Vue.set(parent, path[path.length - 1], module.state); // a.c.e  注册子 module
  }

  module.forEachMutation((mutation, key) => {
    store._mutations[namespaced + key] = store._mutations[namespaced + key] || [];
    store._mutations[namespaced + key].push((payload) => {
      return mutation(module.state, payload);
    });
  });

  // action 执行
  module.forEachAction((action, key) => {
    store._actions[namespaced + key] = store._actions[namespaced + key] || [];
    store._actions[namespaced + key].push(async (payload) => {
      return action(store, payload);
    });
  });

  module.forEachGetters((getter, key) => {
    store._wrappedGetters[key] = function() {
      return getter(module.state); // 计算属性
    };
  });

  // 递归处理孩子    path.concat 不影响原数组
  module.forEachChildren((child, key) => {
    installModule(store, path.concat(key), child, rootState);
  });
};

/**
 * root = {
 *  _raw: 默认用户的原始数据
 * _children: {
 *    a: {_raw: a 模块原始内容， _children: {}, state: aState}
 *    b: {_raw: b模块原始内容， _chidlren: {}, state: bState}
 *  }
 * }
 */

function setStoreVM(store, state) {
  let computed = {};
  store.getters = {}; // 用户最后访问的对象
  forEachValue(store._wrappedGetters, (fn, key) => {
    computed[key] = () => {
      return fn(store.state);
    };
    Object.defineProperty(store.getters, key, {
      get: () => {
        return store._vm[key]; // 返回的计算属性
      },
    });
  });
  store._vm = new Vue({
    data: {
      $$state: state,
    },
    computed,
  });
}

class Store {
  constructor(options) {
    const store = this;
    let state = options.state;

    // 收集所有 actions
    store._actions = {};
    // 收集所有 mutations
    store._mutations = {};

    // 收集用户所有 getters
    store._wrappedGetters = {};

    // 对用户数据 格式化操作
    this._module = new ModuleCollection(options);
    // 从根部开始安装
    installModule(store, [], store._module.root, state);
    setStoreVM(store, state);
    return;

    let getters = options.getters;
    this.getters = {}; // $store.getters.xxxx

    forEachValue(getters, (fn, key) => {
      computed[key] = () => {
        // 将用户属性绑定到 commputed 中
        return fn(this.state);
      };

      // 在组件中实际取的值
      Object.defineProperty(this.getters, key, {
        get: () => {
          return this.vm[key]; // 借助 计算属性 缓存
        },
      });
    });
    // Object.keys(getters).forEach(key => {

    // Object.defineProperty(this.getters, key, {
    //   get: () => { // 会多次执行
    //     return getters[key](this.state)
    //   }
    // })
    // })

    this.vm = new Vue({
      data: {
        $$state: state, // $ 开头的属性不会定义到 vm 上
      },
      computed,
    });

    let mutations = options.mutations;
    this.mutations = {};

    forEachValue(mutations, (fn, key) => {
      // store.commit('changeAge', 100)
      this.mutations[key] = (payload) => fn(this.state, payload);
    });

    this.actions = {};
    let actions = options.actions;
    forEachValue(actions, (fn, key) => {
      this.actions[key] = (payload) => fn(this, payload);
    });
  }

  // this 永远指向 store 容器，不会丢失 this  直接调用  commit
  commit = (type, payload) => {
    // this.mutations[type](payload)
    this._mutations[type].forEach((fn) => fn(payload));
  };

  dispatch = (type, payload) => {
    // this.actions[type](payload)
    // this._actions[type].forEach((fn) => fn(payload));
    return Promise.all(this._actions[type].map((fn) => fn(payload)))
  };

  get state() {
    return this._vm._data.$$state;
  }
}

// Vue.prototype.$store 比较暴力，全部都添加。只希望覆盖自己的子组件
const Vuex = {
  Store,
  install(_Vue) {
    Vue = _Vue;
    // 获取到父组建中定义的 store 属性， 每个组件运行时候都能拿到 store
    Vue.mixin({
      beforeCreate() {
        const options = this.$options;
        console.log(this);
        if (options.store) {
          // 根组件
          this.$store = options.store;
        } else {
          // 要么是子组件  要么是平级组件
          if (this.$parent && this.$parent.$store) {
            // 渲染是从 父 到子执行。根组件 有 $store  后面的子一层一层都会有
            this.$store = this.$parent.$store; // 从父亲属性拿到 $store
          }
        }
      },
    });
  },
};

export default Vuex;

/**
 * 1. vue.mixin 合并  $store
 * 2. 类属性访问器 ，$ 开头 不会挂载到 vm 上， 返回 _data.$$state
 * 3. getters 是计算属性，给 getters 绑定 响应式
 * 4. mutations actions 两个对象，key 绑定 传入的函数。 commit dispatch 根据 type 触发对应的函数
 * 如果没有modules到这就行了
 * 5. 格式化传入的数据，modules 划分，把数据合并
 * 6. 整合 actions mutations getters 合并。install
 * 
 * 7. 判断是否有命名空间，做字符串拼接  a/ + 
 */



/**
 * 插件
 * 
 * a.b.c 就是够贱的父子关系
 */